#ifndef unigame_Vector3_h
#define unigame_Vector3_h

#include <math.h>
#include "math/Math.h"
#include "Vector2.h"

namespace unigame
{
	template <class T>
	class Vector3
	{
	public:
		Vector3():x(0), y(0), z(0){}
		Vector3(T x1, T y1, T z1):x(x1), y(y1), z(z1){}
		~Vector3(){}

		Vector3(const Vector2<T>& v, T z1):x(v.x), y(v.y), z(z1){}
		Vector3(const Vector3<T>& v):x(v.x), y(v.y), z(v.z){}

		void set(T x, T y, T z)
		{
			this->x = x;
			this->y = y;
			this->z = z;
		}
		
		T dot(const Vector3<T>& v) const
		{
			return x * v.x + y * v.y + z * v.z;
		}

		Vector3<T> cross(const Vector3<T>& v) const
		{
			return Vector3<T>(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
		}

		Vector3<T>& operator =(const Vector3<T> v)
		{
			x = v.x;
			y = v.y;
			z = v.z;
			return *this;
		}

		Vector3<T> operator -()
		{
			return Vector3<T>(-x, -y, -z);
		}

		bool operator ==(const Vector3<T>& v)
		{
			return x == v.x && y == v.y && z == v.z;
		}

		bool operator !=(const Vector3<T>& v)
		{
			return x != v.x || y != v.y || z != v.z;
		}

		Vector3<T> operator +(const Vector3<T>& v) const
		{
			return Vector3<T>(x + v.x, y + v.y, z + v.z);
		}

		Vector3<T> operator -(const Vector3<T>& v) const
		{
			return Vector3<T>(x - v.x, y - v.y, z - v.z);
		}

		Vector3<T> operator *(const T value) const
		{
			return Vector3<T>(x * value, y * value, z * value);
		}

		Vector3<T> operator /(const T value) const
		{
			return Vector3<T>(x / value, y / value, z / value);
		}

		Vector3<T>& operator +=(const Vector3<T>& v)
		{
			x += v.x;
			y += v.y;
			z += v.z;
			return *this;
		}

		Vector3<T>& operator -=(const Vector3<T>& v)
		{
			x -= v.x;
			y -= v.y;
			z -= v.z;
			return *this;
		}

		Vector3<T>& operator *=(const T value)
		{
			x *= value;
			y *= value;
			z *= value;
			return *this;
		}

		Vector3<T>& operator /=(const T value)
		{
			x /= value;
			y /= value;
			z /= value;
			return *this;
		}

		T& operator [](const int32u index)
		{
			return getValuePtr()[index];
		}

		T squrMag()
		{
			return x * x + y * y + z * z;
		}

		float mag()
		{
			return 1 / Math::invSqrt(squrMag());
		}

		void normalize()
		{
			float m = Math::invSqrt(squrMag());
			x *= m;
			y *= m;
			z *= m;
		}

	private:
		T* getValuePtr()
		{
			return reinterpret_cast<T*>(this);
		}
	
	public:
		T x;
		T y;
		T z;
	};

	typedef Vector3<float> Vector3f;
}

#endif